# @Time   : 2022-03-03 17:00:00
# @Author : Gavin Jiang
# @Owner  : YSRD (Insights Value)

import os
import re
import json
import logging

from flask import g, Response, request, current_app
import redis
from flask_restful import Resource
from jsonschema.exceptions import ValidationError

from framework.constant.request_method import RequestMethod
from framework.constant.resp_code import RespCode
from framework.decorator.deprecated import deprecated
from framework.interface.abstract_view_model import AbstractViewModel
from framework.util.api_util import ApiUtil
from framework.util.request_json_encoder import RequestJsonEncoder
from framework.util.resp_util import RespUtil
from framework.util.sql_db import SqlDb
from framework.vo.resp_result import RespResult

from werkzeug import exceptions
import werkzeug
from marshmallow import Schema, fields, ValidationError


class AbstractApi(Resource):
    """
    API抽象类，所有api都应继承此类
    """

    # 定义参数规则

    class GetSchema(Schema):
        pass

    class PostSchema(Schema):
        pass

    def redis_conn(self):
        return redis.Redis(
            host=current_app.config['REDIS_HOST'],
            port=current_app.config['REDIS_PORT'],
            password=current_app.config['REDIS_PWD'],
            db=current_app.config['REDIS_DB'])

    def get_domain(self):
        domain_map = {
            'pre': 'https://electricity-api-service-pre.airqualitychina.cn',
            'test': 'http://electricity-api-service.test.hotgrid.cn',
            # 'test': 'https://electricity-api-service-test.airqualitychina.cn',  # 已弃用
            'prod': 'http://electricity-api-service.bjmemc.hotgrid.cn'
        }
        if os.getenv('ENV'):
            domain = domain_map[os.getenv('ENV')]
        else:
            from flask import request
            domain = request.host_url  # 本地运行
        return domain

    def __init__(self, *args, **kwargs):
        self.sql_db = SqlDb()
        self.redis = self.redis_conn()
        self._logger = logging.getLogger(__name__)
        self.domain = self.get_domain()  # 获取当前环境的域名

        super().__init__(*args, **kwargs)

    def decode_params(self, schema):
        """
            接收请求参数, 兼容?json={...} 和 key=value 格式
            1、 请求参数中有json字段
                （1）schema中有json字段，则接收json字段给schema进行验证
                （2）schema中没有json字段，则接收json中的字段给schema进行验证
            2、请求参数中没有json字段
                （1）接收所有请求参数给schema进行验证
        """
        if request.method == "GET":
            if 'json' in request.values:
                if 'json' in schema._declared_fields.keys():
                    params = request.values
                else:
                    params = request.args.get("json")
                    params = params.replace('\'', '\"')
                    params = json.loads(params)
            else:
                params = dict(request.values)
        else:
            if request.json:
                params = request.get_json()
            else:
                params = dict(request.values)
        return params

    def __handle_and_check_params(self):
        if request.method == 'GET':
            schema = self.GetSchema
        else:
            schema = self.PostSchema

        params = self.decode_params(schema)

        try:
            g.params = schema().load(params)
        except ValidationError as error:
            self._logger.warning(str(error))
            return RespUtil.invalid_param(str(error))

    @staticmethod
    def url_names():
        return None

    def get(self):
        return self.__handle_and_resp(RequestMethod.GET)

    def post(self):
        return self.__handle_and_resp(RequestMethod.POST)

    def __handle_and_resp(self, method: RequestMethod):
        # 先对请求进行参数校验，非法则返回，后续添加报错msg
        check_params_result = self.__handle_and_check_params()
        if isinstance(check_params_result, RespResult):
            return check_params_result.build()

        try:
            if method == RequestMethod.POST:
                if current_app.config['_API_NICE_RESP']:
                    result = self.nice_handle_post_req()
                else:
                    result = self.handle_post_request()
                if result is None:
                    result = {}
            else:
                if current_app.config['_API_NICE_RESP']:
                    result = self.nice_handle_get_req()
                else:
                    result = self.handle_get_request()

            if isinstance(result, RespResult):
                return result.build()

            if isinstance(result, Response):
                return result
            if isinstance(result, werkzeug.wrappers.response.Response):
                return result
            return RespUtil.ok(json.loads(json.dumps(result, cls=RequestJsonEncoder))).build()
        except ValidationError as e:
            self._logger.error(RespCode.INVALID_JSON_FORMAT.desc(), exc_info=True)
            return RespUtil.invalid_json_format(e.args[0]).build()
        except Exception as e:
            self._logger.error("内部异常", exc_info=True)
            return RespUtil.inner_error(e.args[0]).build()

    @deprecated
    def handle_get_request(self):
        return RespUtil.request_method_invalid()

    @deprecated
    def handle_post_request(self):
        return RespUtil.request_method_invalid()

    def nice_handle_get_req(self) -> AbstractViewModel:
        log = f'{self.__class__.__name__}类nice_handle_get_req方法未重写'
        logging.error(log)
        raise Exception(log)

    def nice_handle_post_req(self) -> AbstractViewModel:
        log = f'{self.__class__.__name__}类nice_handle_post_req方法未重写'
        logging.error(log)
        raise Exception(f'{self.__class__.__name__}类nice_handle_post_req方法未重写')

    @staticmethod
    def get_params():
        if hasattr(g, 'params'):
            return g.params
        return {}


    @staticmethod
    def get_files(key,default=None):
        return request.files.get(key,default)


    @staticmethod
    def get_param(key, default=None):
        return AbstractApi.get_params().get(key, default)

    @staticmethod
    def tmp_save_req_files(allowed_mimetypes=None, clear_after_request=True):
        return ApiUtil.tmp_save_req_files(allowed_mimetypes, clear_after_request)
